/* Copyright  2009
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
 * @filename: config.cc
 */


#include <server_includes.h>
#include <global.h>
#include <read-config.h>
#include "gettext.h"
#include <debug.h>
#include <clpsar_sys_err.h>
#include <error.h>
#include <errmsg_print.h>

extern "C" {              // Because of SCO 3.2V4.2
#include <errno.h>
#include <sys/stat.h>

}

/* Static Variable */
Config Conf::config;
using namespace BackEnd ;

#define  COLLAPSAR_READLINE_BUFF  2048
#define  ONEK                     1024
/*
    SYNOPSIS:
    int Config::readLine(FILE *fp, char * buffer)
    The private method which is trying to read every line of configuration file.
    @param  fp     File*   the File handle.
    @param  buffer char*   the pointer to the char buffer.The content of line
                           will be scanned into buffer.
*/

int Config::readLine(FILE *fp, char * buffer)
{
  char c =0;
  int _count =0;
  while (1)
  {
      c = ::fgetc(fp);
      if (c == '\n' || c== '\r') break;
      if (c == EOF)  return EOF;
      buffer[_count++] = c;
  }
  return _count;
}

/*
    SYNOPSIS
    int Config::storeKeyVal(char *key, char *value)
        @param in   key   char *  the pointer to the key char.
        @param in   value char *  the pointer to the value char.
        @purpose    store the key/value pair into cVal.
        @return     always return 0

*/
int Config::storeKeyVal(char *key, char *value)
{
    if (strcasestr(key, "PAGE_SIZE") != NULL )
           { cVal.pageSize = atoi(value);  }
    else if (strcasestr(key, "MAX_PROCS") != NULL)
           { cVal.maxProcs = atoi(value);  }
    else if (strcasestr(key, "MAX_SYS_DB_SIZE") != NULL)
           { cVal.maxSysSize = atol(value);  }
    else if (strcasestr(key, "MAX_DB_SIZE") != NULL)
           { cVal.maxDbSize = atol(value);  }
    else if (strcasestr(key, "SYS_DB_KEY") != NULL)
           { cVal.sysDbKey = atoi(value);  }
    else if (strcasestr(key, "USER_DB_KEY") != NULL)
           { cVal.userDbKey = atoi(value);  }
    else if (strcasestr(key, "LOG_FILE") != NULL)
           { strcpy(cVal.logFile , value);  }
    else if (strcasestr(key, "DATABASE_FILE") != NULL)
           { strcpy(cVal.dbFile , value);  }
    else if (strcasestr(key, "MAP_ADDRESS") != NULL)
           { cVal.mapAddr = atol(value);  }
    else if (strcasestr(key, "MUTEX_TIMEOUT_SECS") != NULL)
           { cVal.mutexSecs = atoi(value);  }
    else if (strcasestr(key, "MUTEX_TIMEOUT_USECS") != NULL)
           { cVal.mutexUSecs = atoi(value);  }
    else if (strcasestr(key, "MUTEX_TIMEOUT_RETRIES") != NULL)
           { cVal.mutexRetries = atoi(value);  }
    else if (strcasestr(key, "LOCK_TIMEOUT_SECS") != NULL)
           { cVal.lockSecs = atoi(value);  }
    else if (strcasestr(key, "LOCK_TIMEOUT_USECS") != NULL)
           { cVal.lockUSecs = atoi(value);  }
    else if (strcasestr(key, "LOCK_TIMEOUT_RETRIES") != NULL)
           { cVal.lockRetries = atoi(value);  }
    else if (strcasestr(key, "TABLE_CONFIG_FILE") != NULL)
           { strcpy(cVal.tableConfigFile , value);  }
    else if (strcasestr(key, "CACHE_TABLE") != NULL)
           { cVal.isCache = OS::atobool(value); }
    else if(strcasestr(key,"CACHE_ID")!=NULL)
           { cVal.cacheId = atoi(value);}

    else if (strcasestr(key, "REPLICATION") != NULL)
           { cVal.isReplication = OS::atobool(value); }

    else if (strcasestr(key, "PORT") != NULL)
           { cVal.port = (uint32_t)atoi(value); }
    else if (strcasestr(key, "NETWORK_CONFIG_FILE") != NULL)
           { strcpy(cVal.replConfigFile , value);  }
    else if (strcasestr(key, "MAX_LOG_STORE_SIZE") != NULL)
           { cVal.logStoreSize = atol(value);  }
    else if (strcasestr(key, "MY_NETWORK_ID") != NULL)
           { cVal.networkID = atoi(value);  }
    else if (strcasestr(key, "CACHE_NETWORK_ID") != NULL)
           { cVal.cacheNetworkID = atoi(value);  }
    else if (strcasestr(key, "NETWORK_RESPONSE_TIMEOUT") != NULL)
           { cVal.nwResponseTimeout = atoi(value);  }
    else if (strcasestr(key, "NETWORK_CONNECT_TIMEOUT") != NULL)
           { cVal.nwConnectTimeout = atoi(value);  }
    else if (strcasestr(key, "ENABLE_BIDIRECTIONAL_CACHE") != NULL)
           { cVal.isTwoWay = OS::atobool(value);  }
    else if (strcasestr(key, "CACHE_RECEIVER_WAIT_SECS") != NULL)
           { cVal.cacheWaitSecs = atoi(value);  }
    else if (strcasestr(key, "SERVER_ID") != NULL) /*Get the server id*/
           { cVal.serverId_ = (int32_t) atoi (value);  }
    else if (strcasestr(key, "PLUG-IN_DIR") != NULL) /* Get the plug-in directory*/
           { strcpy(cVal.dbPluginDir,value) ;}
    else if (strcasestr(key, "TMP_DIR") != NULL) /* Get the temp directory*/
           { strcpy(cVal.dbTmpDir,value) ;}
    else if (strcasestr(key, "DB_DATA_DIR") != NULL) /* Get the db data directory*/
           { strcpy(cVal.dbDataDir,value) ;}
    else if (strcasestr(key, "SHARELIB_DIR") != NULL) /* Share lib directory*/
           { strcpy(cVal.dbShareLibDir,value) ;}
    else if (strcasestr(key, "ERROR_LEVEL") != NULL) /* the error/debug level*/
           { cVal.err_level = atoi(value) ;}
    else  return 1;
    return 0;
}
int Config::validateValues()
{
    bool is32bitplatform = true ;
    if(sizeof(void*) >4 ) is32bitplatform = false ;

    if (cVal.pageSize < COLLAPSAR_PAGE_SIZE || cVal.pageSize > 1024 * 1024 * 10 )
    {
        errmsg_printf(ERRMSG_LVL_ERROR,
                                _("PAGE_SIZE should be >= %d and <= 10 MB\n"),COLLAPSAR_PAGE_SIZE);
        return 1;
    }
    if (cVal.pageSize  % (ONEK) )
    {
        errmsg_printf(ERRMSG_LVL_ERROR,
                             _("PAGE_SIZE should be multiples of %d\n"),ONEK);
        return 1;
    }
    if (cVal.maxProcs < 10 || cVal.maxProcs > COLLAPSAR_PAGE_SIZE)
    {
        errmsg_printf(ERRMSG_LVL_ERROR,
            _("MAX_PROCS should be >= 10 and <= %d\n"),COLLAPSAR_PAGE_SIZE);
        return 1;
    }

    if (cVal.maxSysSize % COLLAPSAR_PAGE_SIZE )
    {
        errmsg_printf(ERRMSG_LVL_ERROR,
                      _("MAX_SYS_DB_SIZE should be multiples of %d\n"),
                      COLLAPSAR_PAGE_SIZE);
        return 1;
    }
    if (is32bitplatform){
        if (cVal.maxSysSize < ONEK * ONEK  || cVal.maxSysSize > ONEK *ONEK *ONEK)
      {
        errmsg_printf(ERRMSG_LVL_ERROR,
                        _("MAX_SYS_DB_SIZE should be >= 1 MB and <= 1 GB\n"));
        return 1;
      }
        if (cVal.maxDbSize   < (unsigned) ONEK * ONEK
          ||cVal.maxDbSize   > ((unsigned )2*ONEK*ONEK*ONEK))
      {
        errmsg_printf(ERRMSG_LVL_ERROR,
                           _("MAX_DB_SIZE should be >= 1 MB and <= 2 GB\n"));
        return 1;
      }
    }
    if (cVal.maxDbSize % COLLAPSAR_PAGE_SIZE )
    {
        errmsg_printf(ERRMSG_LVL_ERROR,
                              _("MAX_DB_SIZE should be multiples of %d\n"),
                              COLLAPSAR_PAGE_SIZE);
        return 1;
    }

    if (cVal.sysDbKey < 10 || cVal.sysDbKey > 8192)
    {
        errmsg_printf(ERRMSG_LVL_ERROR,
                                  _("SYS_DB_KEY should be >= 10 and <= %d\n"),
                                  COLLAPSAR_PAGE_SIZE);
        return 1;
    }
    if (cVal.userDbKey < 10 || cVal.userDbKey > 8192)
    {  errmsg_printf(ERRMSG_LVL_ERROR,
            _("USER_DB_KEY should be >= 10 and <= 8192\n"));
        return 1;
    }
    if ( cVal.sysDbKey == cVal.userDbKey)
    {
        errmsg_printf(ERRMSG_LVL_ERROR,
                 _("USER_DB_KEY and SYS_DB_KEY have same value %d\n"),cVal.userDbKey);
        return 1;
    }
    if (0 == strcmp(cVal.logFile,""))
    {
        errmsg_printf(ERRMSG_LVL_ERROR,
                     _("LOG_FILE is set to NULL\n"));
        //TODO::check whether file exists
        //printError(ER_BadArg,  "LOG_FILE is set to NULL");
        return 1;
    }
    if (0 == strcmp(cVal.dbFile,""))
    {
        errmsg_printf(ERRMSG_LVL_ERROR,
                         _("DATABASE_FILE is set to NULL\n"));
        return 1;
    }
    /*TODO: */
    if (cVal.mapAddr < 400000000 || cVal.mapAddr > 2000000000)
    {
       errmsg_printf(ERRMSG_LVL_ERROR,
                             _("MAP_ADDRESS should be >= 400000000 and <= 2000000000\n"));
        return 1;
    }
    if (cVal.mutexSecs < 0 || cVal.mutexSecs > 360)
    {  errmsg_printf(ERRMSG_LVL_ERROR,
            _("MUTEX_TIMEOUT_SECS should be >= 0 and <= 360\n"));
        return 1;
    }
    if (cVal.mutexUSecs < 0 || cVal.mutexUSecs > 1000000)
    {  errmsg_printf(ERRMSG_LVL_ERROR,
            _("MUTEX_TIMEOUT_USECS should be >= 0 and <= 1000000\n"));

        return 1;
    }
    if (cVal.mutexRetries < 0 || cVal.mutexRetries > 100)
    {   errmsg_printf(ERRMSG_LVL_ERROR,
            _("MUTEX_TIMEOUT_RETRY should be >= 0 and <= 100\n"));
        return 1;
    }
    if (cVal.lockSecs < 0 || cVal.lockSecs > 360)
    {
       errmsg_printf(ERRMSG_LVL_ERROR,
                _("LOCK_TIMEOUT_SECS should be >= 0 and <= 360\n"));
        return 1;
    }
    if (cVal.lockUSecs < 0 || cVal.lockUSecs > 1000000)
    {
       errmsg_printf(ERRMSG_LVL_ERROR,
                     _("LOCK_TIMEOUT_USECS should be >= 0 and <= 1000000\n"));
        return 1;
    }
    if (cVal.lockRetries < 0 || cVal.lockRetries > 100)
    {
       errmsg_printf(ERRMSG_LVL_ERROR,
                       _("LOCK_TIMEOUT_RETRY should be >= 0 and <= 100\n"));
        return 1;
    }
    if (cVal.isCache && cVal.isReplication) {
       // printError
       // (ER_BadArg, "Either caching or replication option should be set."
       //                       " Both options are not supported together");
        return 1;
    }
    if (cVal.isReplication || cVal.isCache) {
        if (0 == strcmp(cVal.replConfigFile,""))
        {
            //TODO::check whether file exists
            //printError(ER_BadArg,  "NETWORK_CONFIG_FILE is set to NULL");
            return 1;
        }
        if (0 == strcmp(cVal.tableConfigFile,""))
        {
            //TODO::check whether file exists
            //printError(ER_BadArg,  "TABLE_CONFIG_FILE is set to NULL");
            return 1;
        }
        /*FILE *fp = fopen(cVal.replConfigFile,"r");
        if( fp == NULL ) {
            printError(ErrSysInit, "Invalid path/filename for NETWORK_CONFIG_FILE.\n");
            return 1;
        }
        int count =0;
        int nwid, port;
        char hostname[IDENTIFIER_LENGTH];
        char nwmode;

        while(!feof(fp)) {
            fscanf(fp, "%d:%d:%s\n", &nwid, &port, hostname);
            count++;
        }
        if (count >2) {
            printError(ErrSysInit, "NETWORK_CONFIG_FILE has more than 2 entries\n");
            return 1;
        }*/

    }
    if (cVal.logStoreSize < 1024 * 1024  || cVal.logStoreSize > 1024 *1024 *1024)
    {
        //printError(ER_BadArg,  "MAX_LOG_STORE_SIZE should be >= 1 MB and <= 1 GB");
        return 1;
    }
    if (cVal.logStoreSize % COLLAPSAR_PAGE_SIZE !=0)
    {   errmsg_printf(ERRMSG_LVL_ERROR,
            _("MAX_LOG_STORE_SIZE should be multiples of %d\n"),COLLAPSAR_PAGE_SIZE);
        return 1;
    }
    if (cVal.nwResponseTimeout <0 || cVal.nwResponseTimeout > 60)
    {
        return 1;
    }
    if (cVal.nwConnectTimeout <0 || cVal.nwConnectTimeout > 60)
    {
        return 1;
    }
    if (cVal.cacheWaitSecs <1)
    {
        return 1;
    }
    if (cVal.port <= 1024)
    {
       errmsg_printf(ERRMSG_LVL_ERROR,
                _("Server tcp port should be greater than 1024.\n"));
        return 1;
    }

    if (cVal.serverId_ < 0 || cVal.serverId_ > 65536 )
    {   errmsg_printf(ERRMSG_LVL_ERROR,
            _("Invalid Server Id,the number should be between 0~65536\n"));
        return 1 ;
    }

    if (cVal.err_level < ERRMSG_LVL_DBUG||cVal.err_level>ERRMSG_LVL_ERROR)
    {
        errmsg_printf(ERRMSG_LVL_ERROR,
             _("Invalid error level,the number should be between %d~%d\n"),
             ERRMSG_LVL_DBUG,ERRMSG_LVL_ERROR);
            return 1 ;
    }
    {  /*Checking the directory */
        struct stat filestat;
        if (stat(cVal.dbDataDir, &filestat)||(S_ISDIR(filestat.st_mode) == false))
        {
          errmsg_printf(ERRMSG_LVL_ERROR,
                  _("Invalid Data Directory Name: %s.Please check and use correct absolute path.\n"),cVal.dbDataDir);
           return 1 ;
        }
        if (stat(cVal.dbPluginDir, &filestat)||(S_ISDIR(filestat.st_mode) == false))
        {  errmsg_printf(ERRMSG_LVL_ERROR,
                _("Invalid Plug-in Directory Name: %s.Please check and use correct absolute path.\n"),cVal.dbPluginDir);
           return 1 ;
        }
        if (stat(cVal.dbShareLibDir, &filestat)||(S_ISDIR(filestat.st_mode) == false))
        {  errmsg_printf(ERRMSG_LVL_ERROR,
                _("Invalid Database Share Directory Name: %s.Please check and use correct absolute path.\n"),
                cVal.dbShareLibDir);
           return 1 ;
        }
        if (stat(cVal.dbTmpDir, &filestat)||(S_ISDIR(filestat.st_mode) == false))
        {  errmsg_printf(ERRMSG_LVL_ERROR,
                _("Invalid Database Temp Directory Name: %s.Please check and use correct absolute path.\n"),
                cVal.dbTmpDir);
           return 1 ;
        }
      }
    return 0;
}

/*
    @param in    fileName  char*
                 the pointer to filename char array.

    @return :   0  success
                1  error occurs during the realline operation
*/
int Config::readAllValues(const char *fileName)
{
    FILE *fp;

    fp = fopen(fileName,"r");
    if( fp == NULL ) {
      errmsg_printf(ERRMSG_LVL_ERROR,
                   _("Fatal error.Fail to open configuration file [%s].\n"),fileName);
        return 1;
    }
    errmsg_printf(ERRMSG_LVL_INFO,
                       _("Start to parse configuration file [%s].\n"),fileName);
    int  hasData = 1;
    char buffer[COLLAPSAR_READLINE_BUFF];
    char key   [COLLAPSAR_READLINE_BUFF];
    char value [COLLAPSAR_READLINE_BUFF];


    while (hasData)
    {
        bzero(buffer,COLLAPSAR_READLINE_BUFF);
        int ret = readLine(fp, buffer);
        if (ret == EOF) break;
        bool isComment= false;
        int posEqual =0;
        for (int i = 0; i <COLLAPSAR_READLINE_BUFF; i++)
        {
           if (buffer[i] == '=' ) posEqual=i;
           else if (buffer[i] == '#' ) { isComment = true; break; }
           else if (buffer[i] == '\n'||buffer[i] == '\r') { break; }
           else if (buffer[i] == '\0') { break; }
        }
      if (isComment) continue;
      if (!posEqual) continue;
      strncpy(key, buffer, posEqual);
      key[posEqual] = '\0';
      posEqual++;
      strcpy(value, &buffer[posEqual]);
      /// Call storeKeyVal
      storeKeyVal(key, value);
    }
    fclose(fp);
    /// Start to validate the values.
    errmsg_printf(ERRMSG_LVL_INFO,
                           _("Start to validate configuration values.\n"));
    if (validateValues())
    {
      errmsg_printf(ERRMSG_LVL_ERROR,
           _("Fail to validate configuration file:[%s].Please refer to user manual.\n"),
           fileName);
      return 1;
    }else{
      this->print();  // print the value out.
      errmsg_printf(ERRMSG_LVL_INFO,
              _("Validate configuration file: [%s] successfully.\n"),
               fileName);
    }
    return 0;
}


int Config::readConfigFile(const char *fullfileName)
{
    return readAllValues(fullfileName) ;
}


void Config::print()
{
    errmsg_printf(ERRMSG_LVL_INFO,
                    _("#############Configuration Values#############\n"));
    errmsg_printf(ERRMSG_LVL_INFO,
                        _(" getPageSize %d\n"),getPageSize());
    errmsg_printf(ERRMSG_LVL_INFO,
                     _(" getMaxProcs %d\n"),getMaxProcs());
    errmsg_printf(ERRMSG_LVL_INFO,
                     _(" getMaxSysDbSize %ld\n"),getMaxSysDbSize());
    errmsg_printf(ERRMSG_LVL_INFO,
                       _(" getMaxDbSize %ld\n"),getMaxDbSize());
    errmsg_printf(ERRMSG_LVL_INFO,
                       _(" getSysDbKey %d\n"),getSysDbKey());
    errmsg_printf(ERRMSG_LVL_INFO,
                       _(" getUserDbKey %d\n"),getUserDbKey());
    errmsg_printf(ERRMSG_LVL_INFO,
                       _(" getLogFile %s\n"),getLogFile());
    errmsg_printf(ERRMSG_LVL_INFO,
                      _(" getDatabaseFile %s\n"),getDbFile());
    errmsg_printf(ERRMSG_LVL_INFO,
                      _(" getMapAddress %ld\n"),getMapAddress());
    errmsg_printf(ERRMSG_LVL_INFO,
                          _(" getMutexSecs %d\n"),getMutexSecs());
    errmsg_printf(ERRMSG_LVL_INFO,
                          _(" getMutexUSecs %d\n"),getMutexUSecs());
    errmsg_printf(ERRMSG_LVL_INFO,
                         _(" getMutexRetries %d\n"),getMutexRetries());
    errmsg_printf(ERRMSG_LVL_INFO,
                         _(" getLockSecs %d\n"),getLockSecs());
    errmsg_printf(ERRMSG_LVL_INFO,
                        _(" getLockUSecs %d\n"),getLockUSecs());
    errmsg_printf(ERRMSG_LVL_INFO,
                        _(" getLockRetries %d\n"),getLockRetries());
    errmsg_printf(ERRMSG_LVL_INFO,
                        _(" useCache %d\n"),useCache());
    errmsg_printf(ERRMSG_LVL_INFO,
                       _(" getTableConfigFile %s\n"),getTableConfigFile());
    errmsg_printf(ERRMSG_LVL_INFO,
                           _(" isTwoWayCache %d\n"),useTwoWayCache());
    errmsg_printf(ERRMSG_LVL_INFO,
                           _(" Server Port: %d\n"),getServerPort());
    errmsg_printf(ERRMSG_LVL_INFO,
                               _(" Server Id: %d\n"),getServerId());
    errmsg_printf(ERRMSG_LVL_INFO,
                            _(" Tmp Directory:  %s\n"),getTmpDir());
    errmsg_printf(ERRMSG_LVL_INFO,
                                _(" Data Directory: %s\n"),getDataDir());
    errmsg_printf(ERRMSG_LVL_INFO,
                                _(" Plug-in Directory: %s\n"),getPluginDir());
    errmsg_printf(ERRMSG_LVL_INFO,
                    _(" Share-lib Directory: %s\n"),getShareLibDir());
    errmsg_printf(ERRMSG_LVL_INFO,
                        _("##############################################\n"));
}
